package br.com.simtecnologia.access.controll.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.com.caelum.vraptor.http.route.IllegalRouteException;
import br.com.caelum.vraptor.ioc.ApplicationScoped;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.resource.ResourceMethod;
import br.com.simtecnologia.access.controll.AccessControllUtil;
import br.com.simtecnologia.access.controll.LogicRoute;
import br.com.simtecnologia.access.controll.LoginSession;
import br.com.simtecnologia.access.controll.annotation.DeniedLogic;
import br.com.simtecnologia.access.controll.annotation.LoginLogic;
import br.com.simtecnologia.access.controll.handlers.AccessRouterResolverHandler;

@Component
@ApplicationScoped
public class DefaultAccessRouterResolverHandler implements Serializable, AccessRouterResolverHandler {
	
	private static final long serialVersionUID = -446159589361700719L;
	private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAccessRouterResolverHandler.class);
	private static void debug(String regex, Object ... objects) {
		if (LOGGER.isDebugEnabled())
			LOGGER.debug(String.format(regex, objects));
	}
	
	private final Map<Class<?>, DefaultLogicRoute> loginPageResrouceRoutes = new HashMap<Class<?>, DefaultLogicRoute>();
	private final Map<Class<? extends LoginSession>, DefaultLogicRoute> loginPageDefaultRoutes = new HashMap<Class<? extends LoginSession>, DefaultLogicRoute>();
	
	private final Map<Class<?>, DefaultLogicRoute> deniedPageResrouceRoutes = new HashMap<Class<?>, DefaultLogicRoute>();
	private final Map<Class<? extends LoginSession>, DefaultLogicRoute> deniedPageDefaultRoutes = new HashMap<Class<? extends LoginSession>, DefaultLogicRoute>();
	
	private static final String REGEX_LOGIN_PAGE_RESOURCE = "Existem 2 métodos anotados com @LoginLogic no mesmo recurso [%s] e [%s]";
	private static final String REGEX_LOGIN_PAGE_DEFAULT = "Existem 2 métodos anotados com @LoginLogic(default=%s.class) [%s] e [%s]";
	
	private static final String REGEX_DENIED_PAGE_RESOURCE = "Existem 2 métodos anotados com @DeniedLogic no resurco [%s] e [%s]";
	private static final String REGEX_DENIED_PAGE_DEFAULT = "Existem 2 métodos anotados com @DeniedLogic(default=%s.class) [%s] e [%s]";
	
	
	private static DefaultLogicRoute targetFor(Class<?> clazz, Method method) {
		return new DefaultLogicRoute(clazz, method);
	}
	

	
	@Override
	public void handle(Class<?> annotatedType) {
		for (Method method : annotatedType.getMethods()) {
			LoginLogic loginLogic = method.getAnnotation(LoginLogic.class);
			if (loginLogic != null) {
				addLoginPageResrouce(annotatedType, method);
				addLoginPageDefault(annotatedType, method, loginLogic);
			}
			DeniedLogic deniedLogic = method.getAnnotation(DeniedLogic.class);
			if (deniedLogic != null) {
				addDeniedPageResrouce(annotatedType, method);
				addDeniedPageDefault(annotatedType, method, deniedLogic);
			}
		}
	}
	private static final String REGEX_FOUND_LP = "Found a @LoginLogic [%s]";
	private static final String REGEX_FOUND_DEFAULT_LP = "Found default @LoginLogic [%s] for LoginSession [%s]";
	
	private static final String REGEX_FOUND_DP = "Found a @DeniedLogic [%s]";
	private static final String REGEX_FOUND_DEFAULT_DP = "Found default @DeniedLogic [%s] for LoginSession [%s]";
	
	private void addLoginPageResrouce(Class<?> annotatedType, Method method) {
		debug(REGEX_FOUND_LP, method);
		DefaultLogicRoute oldMethod = loginPageResrouceRoutes.put(annotatedType, targetFor(annotatedType, method));
		if (oldMethod != null)
			throw new IllegalRouteException(String.format(REGEX_LOGIN_PAGE_RESOURCE, 
					method, 
					oldMethod.getLogic()));
	}
	private void addLoginPageDefault(Class<?> annotatedType, Method method, LoginLogic loginLogic) {
		if (loginLogic.defaultFor().equals(LoginSession.class))
			return;
		
		debug(REGEX_FOUND_DEFAULT_LP, method, loginLogic.defaultFor().getName());
		DefaultLogicRoute oldMethod = loginPageDefaultRoutes.put(loginLogic.defaultFor(), targetFor(annotatedType, method));
		if (oldMethod != null)
			throw new IllegalRouteException(String.format(REGEX_LOGIN_PAGE_DEFAULT,
					loginLogic.annotationType().getName(),
					method,
					oldMethod.getLogic()
					));
	}
	
	private void addDeniedPageResrouce(Class<?> annotatedType, Method method) {
		debug(REGEX_FOUND_DP, method);
		DefaultLogicRoute oldMethod = deniedPageResrouceRoutes.put(annotatedType, targetFor(annotatedType, method));
		if (oldMethod != null)
			throw new IllegalRouteException(String.format(REGEX_DENIED_PAGE_RESOURCE, 
					method, 
					oldMethod.getLogic()));
	}
	private void addDeniedPageDefault(Class<?> annotatedType, Method method, DeniedLogic deniedLogic) {
		if (deniedLogic.defaultFor().equals(LoginSession.class))
			return;
		debug(REGEX_FOUND_DEFAULT_DP,method, deniedLogic.defaultFor().getName());
		DefaultLogicRoute oldMethod = deniedPageDefaultRoutes.put(deniedLogic.defaultFor(), targetFor(annotatedType, method));
		if (oldMethod != null)
			throw new IllegalRouteException(String.format(REGEX_DENIED_PAGE_DEFAULT,
					deniedLogic.annotationType().getSimpleName(),
					method,
					oldMethod
					));
	}
	
	@Override
	public LogicRoute registerLogicRouteDenied(ResourceMethod method) {
		LogicRoute target = deniedPageResrouceRoutes.get(method.getResource().getType());
		if (target == null)
			target = deniedPageDefaultRoutes.get(AccessControllUtil.getSessionClassFromResource(method));
		
		return target;
	}
	
	@Override
	public LogicRoute registerLogicRouteLogin(ResourceMethod method) {
		LogicRoute target = loginPageResrouceRoutes.get(method.getResource().getType());
		if (target == null)
			target = loginPageDefaultRoutes.get(AccessControllUtil.getSessionClassFromResource(method));
		
		return target;
	}
}
